﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.IO;

namespace ConfigGen.Utilities.Extensions.System.IO
{
    /// <summary>
    /// Extension methods for the <see cref="DirectoryInfo"/> class.
    /// </summary>
    public static class DirectoryInfoExtensions
    {
        /// <summary>
        /// Copies the contents of the source directory to the specified target directory. 
        /// The target directory is created if it doesn't exist.
        /// </summary>
        /// <remarks>When copying recursively, this uses a naive case-insensitive <see cref="FileSystemInfo.FullName"/>
        /// check to ensure it doesn't get caught in a recursive loop if the specified target directory is a sub-directory
        /// of the source directory. This takes no account of soft/hard links and so may fail if they are used.
        /// </remarks>
        /// <param name="sourceDirectory">Source directory</param>
        /// <param name="targetDirectory">Path to target directory</param>
        /// <param name="recursive">True to copy recursively, otherwise false</param>
        /// <param name="overwriteFiles">True to overwrite files in the target directory, otherwise false</param>
        /// <returns>Number of files copied</returns>
        /// <exception cref="ArgumentNullException">Thrown if either <paramref name="sourceDirectory"/> or <paramref name="targetDirectory"/> is null</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="targetDirectory"/> is zero length</exception>
        public static int CopyContentsTo ( this DirectoryInfo sourceDirectory, string targetDirectory, bool recursive, bool overwriteFiles)
        {
            return CopyContentsTo(sourceDirectory, targetDirectory, recursive, overwriteFiles, (f) => true);
        }

        /// <summary>
        /// Copies the contents of the source directory to the specified target directory. 
        /// The target directory is created if it doesn't exist.
        /// </summary>
        /// <remarks>When copying recursively, this uses a naive case-insensitive <see cref="FileSystemInfo.FullName"/>
        /// check to ensure it doesn't get caught in a recursive loop if the specified target directory is a sub-directory
        /// of the source directory. This takes no account of soft/hard links and so may fail if they are used.
        /// </remarks>
        /// <param name="sourceDirectory">Source directory</param>
        /// <param name="targetDirectory">Path to target directory</param>
        /// <param name="recursive">True to copy recursively, otherwise false</param>
        /// <param name="overwriteFiles">True to overwrite files in the target directory, otherwise false</param>
        /// <param name="copyIf">An function that must evaluate to true to copy a specified file.</param>
        /// <returns>Number of files copied</returns>
        /// <exception cref="ArgumentNullException">Thrown if either <paramref name="sourceDirectory"/> or <paramref name="targetDirectory"/> is null</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="targetDirectory"/> is zero length</exception>
        public static int CopyContentsTo ( this DirectoryInfo sourceDirectory, string targetDirectory, bool recursive, bool overwriteFiles, Func<FileInfo, bool> copyIf)
        {
            if (sourceDirectory == null) throw new ArgumentNullException("sourceDirectory");
            if (targetDirectory == null) throw new ArgumentNullException("targetDirectory");
            if (targetDirectory.Length == 0) throw new ArgumentException("targetDirectory cannot be zero length");

            var target = new DirectoryInfo(targetDirectory);
            if (!target.Exists)
            {
                target.Create();
                target.Refresh();
            }

            int copied = 0;
            if (recursive)
            {
                foreach (var subDirectory in sourceDirectory.GetDirectories())
                {
                    if (!string.Equals(subDirectory.FullName, target.FullName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        var targetSubdirectory = Path.Combine(target.FullName, subDirectory.Name);
                        copied += subDirectory.CopyContentsTo(targetSubdirectory, true, overwriteFiles, copyIf);
                    }
                }
            }

            foreach (var file in sourceDirectory.GetFiles())
            {
                if (copyIf(file))
                {
                    file.CopyTo(Path.Combine(target.FullName, file.Name), overwriteFiles);
                    copied++;
                }
            }

            return copied;
        }
    }
}
